﻿using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Actions;
using DevExpress.ExpressApp.Editors;
using DevExpress.ExpressApp.Xpo;
using DevExpress.Persistent.Base;
using DevExpress.Xpo;
using DevExpress.Xpo.DB;
using EasyXaf.EntityDesigner.BusinessObjects;
using EasyXaf.EntityDesigner.CodeTemplates;
using EasyXaf.EntityDesigner.Tools;

namespace EasyXaf.EntityDesigner.Controllers;

public class DomainModelViewController : ObjectViewController<ListView, DomainModel>
{
    public SingleChoiceAction DomainModelToolsAction { get; }

    public DomainModelViewController()
    {
        DomainModelToolsAction = new SingleChoiceAction(this, $"{Name}-{nameof(DomainModelToolsAction)}", PredefinedCategory.Tools)
        {
            Caption = "工具",
            ImageName = "Actions_Options",
            ItemType = SingleChoiceActionItemType.ItemIsOperation
        };

        DomainModelToolsAction.Items.Add(new ChoiceActionItem("ExportCode", "导出模型代码", null) { ImageName = "Export" });
        DomainModelToolsAction.Items.Add(new ChoiceActionItem("ExportDomainModel", "导出领域模型", null) { ImageName = "Export", BeginGroup = true });
        DomainModelToolsAction.Items.Add(new ChoiceActionItem("ImportDomainModel", "导入领域模型", null) { ImageName = "Import" });
        DomainModelToolsAction.Items.Add(new ChoiceActionItem("ExportDesignerDomainModel", "导出设计器模型", null) { ImageName = "Export", BeginGroup = true });
    }

    protected override void OnViewControlsCreated()
    {
        base.OnViewControlsCreated();

        var referenceModelsViewItem = View.FindItem("ReferenceModels");
        if (referenceModelsViewItem is ListPropertyEditor referenceModelsEditor)
        {
            referenceModelsEditor.ControlCreated += ListPropertyEditor_ControlCreated;
        }
    }

    protected override void OnActivated()
    {
        base.OnActivated();

        DomainModelToolsAction.Execute += ExportChoiceAction_Execute;
    }

    protected override void OnDeactivated()
    {
        DomainModelToolsAction.Execute -= ExportChoiceAction_Execute;

        base.OnDeactivated();
    }

    private static List<DomainModel> GetReferenceModels(DomainModel domainModel)
    {
        var referenceDomainModels = new List<DomainModel>();
        foreach (var referenceDomainModel in domainModel.ReferenceModels)
        {
            referenceDomainModels.Add(referenceDomainModel);
            referenceDomainModels.AddRange(GetReferenceModels(referenceDomainModel));
        }
        return referenceDomainModels.Distinct().ToList();
    }

    private static UnitOfWork CreateSession(string dbFile, out Action disposeAction)
    {
        var dataStore = SQLiteConnectionProvider.CreateProviderFromString(
            $"Data Source=\"{dbFile}\";Pooling=False",
            AutoCreateOption.DatabaseAndSchema,
            out var objectsToDisposeOnDisconnect);

        var dataLayer = new SimpleDataLayer(dataStore);
        dataLayer.Dictionary.CollectClassInfos(typeof(XpoDomainModelGenerator).Assembly);
        var session = new UnitOfWork(dataLayer);

        disposeAction = () =>
        {
            dataLayer.Dispose();
            session.Dispose();
            foreach (var disposableObject in objectsToDisposeOnDisconnect)
            {
                disposableObject.Dispose();
            }
        };

        return session;
    }

    private void SelectExportPath(Action<string> action)
    {
        var objectSpace = Application.CreateObjectSpace(typeof(ExportDomainModelObject));
        var obj = objectSpace.CreateObject<ExportDomainModelObject>();
        var view = Application.CreateDetailView(objectSpace, obj);
        Application.ShowViewStrategy.ShowViewInPopupWindow(view, () =>
        {
            if (string.IsNullOrWhiteSpace(obj.Path))
            {
                throw new Exception("路径不能为空");
            }

            if (File.Exists(obj.Path))
            {
                File.Delete(obj.Path);
            }

            Directory.CreateDirectory(Path.GetDirectoryName(obj.Path));
            action?.Invoke(obj.Path);
        });
    }

    private void ExportCode()
    {
        if (View.SelectedObjects.Count == 0)
        {
            throw new Exception("选择需要导出代码的领域模型");
        }

        var objectSpace = Application.CreateObjectSpace(typeof(ExportCodeObject));
        var obj = objectSpace.CreateObject<ExportCodeObject>();
        var view = Application.CreateDetailView(objectSpace, obj);
        Application.ShowViewStrategy.ShowViewInPopupWindow(view, () =>
        {
            if (string.IsNullOrWhiteSpace(obj.Path))
            {
                throw new Exception("路径不能为空");
            }

            foreach (var domainModel in View.SelectedObjects.OfType<DomainModel>())
            {
                domainModel.ExportCode(obj.Path, obj.GenerateExtensionEntity);
            }
        });
    }

    private void ExportDomainModel()
    {
        if (View.SelectedObjects.Count == 0)
        {
            throw new Exception("选择需要导出的领域模型");
        }

        var selectedDomainModels = View.SelectedObjects.OfType<DomainModel>().ToList();
        foreach (var selectedDomainModel in selectedDomainModels.ToList())
        {
            foreach (var referenceDomainModel in GetReferenceModels(selectedDomainModel))
            {
                selectedDomainModels.Remove(referenceDomainModel);
            }
        }

        SelectExportPath(path =>
        {
            var session = CreateSession(path, out var disposeAction);
            foreach (var selectedDomainModel in selectedDomainModels)
            {
                new DomainModelCloner(session).CloneTo(selectedDomainModel, typeof(DomainModel));
            }
            session.CommitChanges();
            disposeAction();
        });
    }

    private void ImportDomainModel()
    {
        var objectSpace = Application.CreateObjectSpace(typeof(ImportDomainModelObject));
        var obj = objectSpace.CreateObject<ImportDomainModelObject>();
        var view = Application.CreateDetailView(objectSpace, obj);
        Application.ShowViewStrategy.ShowViewInPopupWindow(view, () =>
        {
            if (string.IsNullOrWhiteSpace(obj.Path))
            {
                throw new Exception("路径不能为空");
            }

            if (!File.Exists(obj.Path))
            {
                throw new Exception("路径不存在");
            }

            var session = CreateSession(obj.Path, out var disposeAction);
            var domainModelObjects = session.Query<DomainModel>().Select(m => new DomainModelObject
            {
                Id = m.Oid,
                Name = m.Name,
                Version = m.Version,
                Description = m.Description,
            }).ToList();

            disposeAction();

            objectSpace = Application.CreateObjectSpace(typeof(DomainModelObject));
            var collectionSource = new CollectionSource(objectSpace, typeof(DomainModelObject));

            foreach (var domainModelObject in domainModelObjects)
            {
                collectionSource.Add(domainModelObject);
            }

            var listViewId = Application.GetListViewId(typeof(DomainModelObject));
            var listView = Application.CreateListView(listViewId, collectionSource, true);

            Application.ShowViewStrategy.ShowViewInPopupWindow(listView, () =>
            {
                var session = CreateSession(obj.Path, out var disposeAction);

                var domainModelObjects = listView.SelectedObjects.OfType<DomainModelObject>().ToList();
                var domainModels = session.Query<DomainModel>().ToList().Where(m => domainModelObjects.Any(s => s.Id == m.Oid)).ToList();

                foreach (var domainModel in domainModels.ToList())
                {
                    foreach (var referenceDomainModel in GetReferenceModels(domainModel))
                    {
                        domainModels.Remove(referenceDomainModel);
                    }
                }

                using var objectSpace = (XPObjectSpace)Application.CreateObjectSpace(typeof(DomainModel));

                foreach (var domainModel in domainModels)
                {
                    if (!objectSpace.GetObjectsQuery<DomainModel>(false).Any(m => m.Oid == domainModel.Oid))
                    {
                        new DomainModelCloner(objectSpace.Session).CloneTo(domainModel, typeof(DomainModel));
                    }
                }

                objectSpace.CommitChanges();

                disposeAction();

                View.Refresh(true);
            });
        });
    }

    private void ExportDesignerDomainModel()
    {
        SelectExportPath(path =>
        {
            XpoDomainModelGenerator.Generate(Application.TypesInfo, path);
        });
    }

    private void ExportChoiceAction_Execute(object sender, SingleChoiceActionExecuteEventArgs e)
    {
        if (e.SelectedChoiceActionItem.Id == "ExportCode")
        {
            ExportCode();
        }
        else if (e.SelectedChoiceActionItem.Id == "ExportDomainModel")
        {
            ExportDomainModel();
        }
        else if (e.SelectedChoiceActionItem.Id == "ImportDomainModel")
        {
            ImportDomainModel();
        }
        else if (e.SelectedChoiceActionItem.Id == "ExportDesignerDomainModel")
        {
            ExportDesignerDomainModel();
        }
    }

    private void ListPropertyEditor_ControlCreated(object sender, EventArgs e)
    {
        if (sender is ListPropertyEditor editor)
        {
            var changeDomainModelVersionViewController = editor.Frame.GetController<ChangeDomainModelVersionViewController>();
            if (changeDomainModelVersionViewController != null)
            {
                changeDomainModelVersionViewController.MasterDomainModel = ViewCurrentObject;
            }
        }
    }
}
